use std::cell::{RefCell};
use std::rc::Rc;
use crate::byte_data_reader::ByteDataReader;
use crate::cf_constant_info::{CFConstantInfoEnum};

/// ```
/// ConstantValue_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 constantvalue_index;
/// }
/// ```
pub struct AttributeInfoConstantValue {
    constant_value_index: u16,
}

/// ```
/// Code_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 max_stack;
///     u2 max_locals;
///     u4 code_length;
///     u1 code[code_length];
///     u2 exception_table_length;
///     {   u2 start_pc;
///         u2 end_pc;
///         u2 handler_pc;
///         u2 catch_type;
///     } exception_table[exception_table_length];
///     u2 attributes_count;
///     attribute_info attributes[attributes_count];
/// }
/// ```
pub struct AttributeInfoCode {
    pub max_stack: u16,
    pub max_locals: u16,
    pub code: Rc<RefCell<Vec<u8>>>,
    pub exception_table: RefCell<Vec<AttributeInfoCodeExceptionTableEntry>>,
    pub attributes: RefCell<Vec<AttributeInfoEnum>>,
}

pub struct AttributeInfoCodeExceptionTableEntry {
    start_pc: u16,
    end_pc: u16,
    handler_pc: u16,
    catch_type: u16,
}

/// Exceptions_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 number_of_exceptions;
///     u2 exception_index_table[number_of_exceptions];
/// }
pub struct AttributeInfoExceptions {
    exception_index_table: RefCell<Vec<u16>>,
}

/// InnerClasses_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 number_of_classes;
///     {   u2 inner_class_info_index;
///         u2 outer_class_info_index;
///         u2 inner_name_index;
///         u2 inner_class_access_flags;
///     } classes[number_of_classes];
/// }
pub struct AttributeInfoInnerClasses {
    classes: RefCell<Vec<AttributeInfoInnerClassesEntry>>,
}

pub struct AttributeInfoInnerClassesEntry {
    inner_class_info_index: u16,
    outer_class_info_index: u16,
    inner_name_index: u16,
    inner_class_access_flags: u16,
}

/// EnclosingMethod_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 class_index;
///     u2 method_index;
/// }
pub struct AttributeInfoEnclosingMethod {
    class_index: u16,
    method_index: u16,
}

/// Synthetic_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
/// }
pub struct AttributeInfoSynthetic {}

const ATTRIBUTE_INFO_SYNTHETIC: AttributeInfoSynthetic = AttributeInfoSynthetic {};

/// Signature_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 signature_index;
/// }
pub struct AttributeInfoSignature {
    signature_index: u16,
}

/// SourceFile_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 sourcefile_index;
/// }
pub struct AttributeInfoSourceFile {
    sourcefile_index: u16,
}

/// LineNumberTable_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 line_number_table_length;
///     {   u2 start_pc;
///         u2 line_number;
///     } line_number_table[line_number_table_length];
/// }
pub struct AttributeInfoLineNumberTable {
    line_number_table: RefCell<Vec<AttributeInfoLineNumberTableEntry>>,
}

pub struct AttributeInfoLineNumberTableEntry {
    start_pc: u16,
    line_number: u16,
}

/// LocalVariableTable_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 local_variable_table_length;
///     {   u2 start_pc;
///         u2 length;
///         u2 name_index;
///         u2 descriptor_index;
///         u2 index;
///     } local_variable_table[local_variable_table_length];
/// }
pub struct AttributeInfoLocalVariableTable {
    local_variable_table: RefCell<Vec<AttributeInfoLocalVariableTableEntry>>,
}

pub struct AttributeInfoLocalVariableTableEntry {
    start_pc: u16,
    length: u16,
    name_index: u16,
    descriptor_index: u16,
    index: u16,
}

/// LocalVariableTypeTable_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 local_variable_type_table_length;
///     {   u2 start_pc;
///         u2 length;
///         u2 name_index;
///         u2 signature_index;
///         u2 index;
///     } local_variable_type_table[local_variable_type_table_length];
/// }

pub struct AttributeInfoLocalVariableTypeTable {
    local_variable_type_table: RefCell<Vec<AttributeInfoLocalVariableTypeTableEntry>>,
}

pub struct AttributeInfoLocalVariableTypeTableEntry {
    start_pc: u16,
    length: u16,
    name_index: u16,
    signature_index: u16,
    index: u16,
}

/// Deprecated_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
/// }
pub struct AttributeInfoDeprecated {}

const ATTRIBUTE_INFO_DEPRECATED: AttributeInfoDeprecated = AttributeInfoDeprecated {};

/// BootstrapMethods_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u2 num_bootstrap_methods;
///     {   u2 bootstrap_method_ref;
///         u2 num_bootstrap_arguments;
///         u2 bootstrap_arguments[num_bootstrap_arguments];
///     } bootstrap_methods[num_bootstrap_methods];
/// }
pub struct AttributeInfoBootstrapMethods {
    bootstrap_methods: RefCell<Vec<AttributeInfoBootstrapMethodsEntry>>,
}

pub struct AttributeInfoBootstrapMethodsEntry {
    bootstrap_method_ref: u16,
    bootstrap_arguments: RefCell<Vec<u16>>,
}

/// MethodParameters_attribute {
///     u2 attribute_name_index;
///     u4 attribute_length;
///     u1 parameters_count;
///     {   u2 name_index;
///         u2 access_flags;
///     } parameters[parameters_count];
/// }
pub struct AttributeInfoMethodParameters {
    parameters: RefCell<Vec<AttributeInfoMethodParametersItem>>,
}

pub struct AttributeInfoMethodParametersItem {
    name_index: u16,
    access_flags: u16,
}

/// RuntimeVisibleAnnotations_attribute {
///     u2         attribute_name_index;
///     u4         attribute_length;
///     u2         num_annotations;
///     annotation annotations[num_annotations];
/// }
/// annotation {
///     u2 type_index;
///     u2 num_element_value_pairs;
///     {   u2            element_name_index;
///         element_value value;
///     } element_value_pairs[num_element_value_pairs];
/// }
/// element_value {
///     u1 tag;
///     union {
///         u2 const_value_index;
///
///         {   u2 type_name_index;
///             u2 const_name_index;
///         } enum_const_value;
///
///         u2 class_info_index;
///
///         annotation annotation_value;
///
///         {   u2            num_values;
///             element_value values[num_values];
///         } array_value;
///     } value;
/// }
pub struct AttributeInfoRuntimeVisibleAnnotations {
    annotations: RefCell<Vec<RuntimeVisibleAnnotation>>,
}

pub struct RuntimeVisibleAnnotation {
    type_index: u16,
    element_value_pairs: RefCell<Vec<RuntimeVisibleAnnotationElementValuePair>>,
}

pub struct RuntimeVisibleAnnotationElementValuePair {
    element_name_index: u16,
    value: RuntimeVisibleAnnotationElementValuePairElementValue,
}

pub struct RuntimeVisibleAnnotationElementValuePairElementValue {
    tag: u8,
    const_value_index: u16,
    // constant value
    enum_const_value_type_name_index: u16,
    enum_const_value_const_name_index: u16,
    // class info
    class_info_index: u16,
    // annotation
    annotation_value: Option<Box<RuntimeVisibleAnnotationElementValuePairElementValue>>,
    // array
    element_value: Option<RefCell<Vec<RuntimeVisibleAnnotationElementValuePairElementValue>>>,
}


pub struct AttributeInfoUnparsed {}

pub enum AttributeInfoEnum {
    AttributeInfoConstantValue { val: AttributeInfoConstantValue },
    AttributeInfoCode { val: AttributeInfoCode },
    AttributeInfoExceptions { val: AttributeInfoExceptions },
    AttributeInfoInnerClasses { val: AttributeInfoInnerClasses },
    AttributeInfoEnclosingMethod { val: AttributeInfoEnclosingMethod },
    AttributeInfoSynthetic { val: AttributeInfoSynthetic },
    AttributeInfoSignature { val: AttributeInfoSignature },
    AttributeInfoSourceFile { val: AttributeInfoSourceFile },
    AttributeInfoLineNumberTable { val: AttributeInfoLineNumberTable },
    AttributeInfoLocalVariableTable { val: AttributeInfoLocalVariableTable },
    AttributeInfoLocalVariableTypeTable { val: AttributeInfoLocalVariableTypeTable },
    AttributeInfoDeprecated { val: AttributeInfoDeprecated },
    AttributeInfoBootstrapMethods { val: AttributeInfoBootstrapMethods },
    AttributeInfoMethodParameters { val: AttributeInfoMethodParameters },
    AttributeInfoRuntimeVisibleAnnotations { val: AttributeInfoRuntimeVisibleAnnotations },
    AttributeInfoUnparsed { val: AttributeInfoUnparsed },
}

pub fn create_attribute_info(reader: &mut ByteDataReader, cf_constant_pool: &RefCell<Vec<CFConstantInfoEnum>>)
                             -> Result<AttributeInfoEnum, String> {
    let ro_cf_constant_pool = cf_constant_pool.borrow();
    let attr_name_index = reader.read_u16() as usize;
    let _attr_len = reader.read_u32();
    let constant_info_utf8 = ro_cf_constant_pool.get(attr_name_index).unwrap();

    let attr_name = match constant_info_utf8 {
        CFConstantInfoEnum::CFConstantInfoUTF8(info) => {
            String::from(&info.string)
        }
        _ => {
            String::from("Error")
        }
    };

    return match attr_name.as_str() {
        "ConstantValue" => {
            Ok(AttributeInfoEnum::AttributeInfoConstantValue {
                val: AttributeInfoConstantValue {
                    constant_value_index: reader.read_u16()
                }
            })
        }
        "Code" => {
            let max_stack = reader.read_u16();
            let max_locals = reader.read_u16();
            let code_length = reader.read_u32();
            let code = reader.read_bytes(code_length);
            let exception_table_length = reader.read_u16();
            let exception_table: RefCell<Vec<AttributeInfoCodeExceptionTableEntry>> = RefCell::new(Vec::new());
            for _ in 0..exception_table_length {
                exception_table.borrow_mut().push(AttributeInfoCodeExceptionTableEntry {
                    start_pc: reader.read_u16(),
                    end_pc: reader.read_u16(),
                    handler_pc: reader.read_u16(),
                    catch_type: reader.read_u16(),
                });
            }
            let attributes_count = reader.read_u16();
            let attributes: RefCell<Vec<AttributeInfoEnum>> = RefCell::new(Vec::new());
            for _ in 0..attributes_count {
                let create_attribute_info_result = create_attribute_info(reader, cf_constant_pool);
                match create_attribute_info_result {
                    Ok(attr) => {
                        attributes.borrow_mut().push(attr);
                    }
                    Err(err) => {
                        println!("{}", err)
                    }
                }
            }
            Ok(AttributeInfoEnum::AttributeInfoCode {
                val: AttributeInfoCode {
                    max_stack,
                    max_locals,
                    code: Rc::new(code),
                    exception_table,
                    attributes,
                }
            })
        }
        "Exceptions" => {
            let number_of_exceptions = reader.read_u16();
            let exception_index_table: RefCell<Vec<u16>> = RefCell::new(Vec::new());
            for _ in 0..number_of_exceptions {
                exception_index_table.borrow_mut().push(reader.read_u16());
            }
            Ok(AttributeInfoEnum::AttributeInfoExceptions {
                val: AttributeInfoExceptions {
                    exception_index_table
                }
            })
        }
        "InnerClasses" => {
            let number_of_classes = reader.read_u16();
            let classes: RefCell<Vec<AttributeInfoInnerClassesEntry>> = RefCell::new(Vec::new());
            for _ in 0..number_of_classes {
                classes.borrow_mut().push(AttributeInfoInnerClassesEntry {
                    inner_class_info_index: reader.read_u16(),
                    outer_class_info_index: reader.read_u16(),
                    inner_name_index: reader.read_u16(),
                    inner_class_access_flags: reader.read_u16(),
                });
            }
            Ok(AttributeInfoEnum::AttributeInfoInnerClasses {
                val: AttributeInfoInnerClasses {
                    classes
                }
            })
        }
        "EnclosingMethod" => {
            let class_index = reader.read_u16();
            let method_index = reader.read_u16();
            Ok(AttributeInfoEnum::AttributeInfoEnclosingMethod {
                val: AttributeInfoEnclosingMethod {
                    class_index,
                    method_index,
                }
            })
        }
        "Synthetic" => {
            Ok(AttributeInfoEnum::AttributeInfoSynthetic { val: ATTRIBUTE_INFO_SYNTHETIC })
        }
        "Signature" => {
            let signature_index = reader.read_u16();
            Ok(AttributeInfoEnum::AttributeInfoSignature {
                val: AttributeInfoSignature {
                    signature_index
                }
            })
        }
        "SourceFile" => {
            let sourcefile_index = reader.read_u16();
            Ok(AttributeInfoEnum::AttributeInfoSourceFile {
                val: AttributeInfoSourceFile {
                    sourcefile_index
                }
            })
        }
        "LineNumberTable" => {
            let line_number_table_length = reader.read_u16();
            let line_number_table: RefCell<Vec<AttributeInfoLineNumberTableEntry>> = RefCell::new(Vec::new());
            for _ in 0..line_number_table_length {
                line_number_table.borrow_mut().push(AttributeInfoLineNumberTableEntry {
                    start_pc: reader.read_u16(),
                    line_number: reader.read_u16(),
                })
            }
            Ok(AttributeInfoEnum::AttributeInfoLineNumberTable {
                val: AttributeInfoLineNumberTable {
                    line_number_table
                }
            })
        }
        "LocalVariableTable" => {
            let local_variable_table_length = reader.read_u16();
            let local_variable_table: RefCell<Vec<AttributeInfoLocalVariableTableEntry>> = RefCell::new(Vec::new());
            for _ in 0..local_variable_table_length {
                local_variable_table.borrow_mut().push(AttributeInfoLocalVariableTableEntry {
                    start_pc: reader.read_u16(),
                    length: reader.read_u16(),
                    name_index: reader.read_u16(),
                    descriptor_index: reader.read_u16(),
                    index: reader.read_u16(),
                })
            }
            Ok(AttributeInfoEnum::AttributeInfoLocalVariableTable {
                val: AttributeInfoLocalVariableTable {
                    local_variable_table
                }
            })
        }
        "LocalVariableTypeTable" => {
            let local_variable_type_table_length = reader.read_u16();
            let local_variable_type_table: RefCell<Vec<AttributeInfoLocalVariableTypeTableEntry>> = RefCell::new(Vec::new());
            for _ in 0..local_variable_type_table_length {
                local_variable_type_table.borrow_mut().push(AttributeInfoLocalVariableTypeTableEntry {
                    start_pc: reader.read_u16(),
                    length: reader.read_u16(),
                    name_index: reader.read_u16(),
                    signature_index: reader.read_u16(),
                    index: reader.read_u16(),
                });
            }
            Ok(AttributeInfoEnum::AttributeInfoLocalVariableTypeTable {
                val: AttributeInfoLocalVariableTypeTable {
                    local_variable_type_table
                }
            })
        }
        "Deprecated" => {
            Ok(AttributeInfoEnum::AttributeInfoDeprecated {
                val: ATTRIBUTE_INFO_DEPRECATED
            })
        }
        "BootstrapMethods" => {
            let num_bootstrap_methods = reader.read_u16();
            let bootstrap_methods: RefCell<Vec<AttributeInfoBootstrapMethodsEntry>> = RefCell::new(Vec::new());
            for _ in 0..num_bootstrap_methods {
                let bootstrap_method_ref = reader.read_u16();
                let num_bootstrap_arguments = reader.read_u16();
                let bootstrap_arguments: RefCell<Vec<u16>> = RefCell::new(vec![]);

                for _ in 0..num_bootstrap_arguments {
                    bootstrap_arguments.borrow_mut().push(reader.read_u16());
                }
                bootstrap_methods.borrow_mut().push(AttributeInfoBootstrapMethodsEntry {
                    bootstrap_method_ref,
                    bootstrap_arguments,
                });
            }
            Ok(AttributeInfoEnum::AttributeInfoBootstrapMethods {
                val: AttributeInfoBootstrapMethods {
                    bootstrap_methods
                }
            })
        }
        "MethodParameters" => {
            let parameters_count = reader.read_u8();
            let parameters: RefCell<Vec<AttributeInfoMethodParametersItem>> = RefCell::new(Vec::new());
            for _ in 0..parameters_count {
                parameters.borrow_mut().push(AttributeInfoMethodParametersItem {
                    name_index: reader.read_u16(),
                    access_flags: reader.read_u16(),
                });
            }
            Ok(AttributeInfoEnum::AttributeInfoMethodParameters {
                val: AttributeInfoMethodParameters {
                    parameters
                }
            })
        }
        "RuntimeVisibleAnnotations" => {
            let num_annotations = reader.read_u16();
            let annotations: RefCell<Vec<RuntimeVisibleAnnotation>> = RefCell::new(Vec::new());
            for _ in 0..num_annotations {
                let type_index = reader.read_u16();
                let num_element_value_pairs = reader.read_u16();
                let element_value_pairs: RefCell<Vec<RuntimeVisibleAnnotationElementValuePair>> = RefCell::new(Vec::new());
                for _ in 0..num_element_value_pairs {
                    let element_name_index = reader.read_u16();
                    let anno_result = parse_runtime_visible_annotation_element_value_pairs_element_value(reader);
                    if let Err(msg) = anno_result {
                        return Err(msg);
                    }
                    let value = anno_result.unwrap();
                    element_value_pairs.borrow_mut().push(RuntimeVisibleAnnotationElementValuePair {
                        element_name_index,
                        value,
                    });
                }
                annotations.borrow_mut().push(RuntimeVisibleAnnotation {
                    type_index,
                    element_value_pairs,
                });
            }
            Ok(AttributeInfoEnum::AttributeInfoRuntimeVisibleAnnotations {
                val: AttributeInfoRuntimeVisibleAnnotations {
                    annotations
                }
            })
        }
        _ => {
            let _ = reader.read_bytes(_attr_len);
            Ok(AttributeInfoEnum::AttributeInfoUnparsed {
                val: AttributeInfoUnparsed {}
            })
        }
    };
}


fn parse_runtime_visible_annotation_element_value_pairs_element_value(reader: &mut ByteDataReader)
                                                                      -> Result<RuntimeVisibleAnnotationElementValuePairElementValue, String> {
    // 解析element_value
    let u8_tag = reader.read_u8();
    let tag = char::from(u8_tag);
    return match tag {
        'B' | 'C' | 'D' | 'F' | 'I' | 'J' | 'S' | 'Z' | 's' => {
            Ok(RuntimeVisibleAnnotationElementValuePairElementValue {
                tag: u8_tag,
                const_value_index: reader.read_u16(),
                enum_const_value_type_name_index: 0,
                enum_const_value_const_name_index: 0,
                class_info_index: 0,
                annotation_value: None,
                element_value: None,
            })
        }
        'e' => {
            Ok(RuntimeVisibleAnnotationElementValuePairElementValue {
                tag: u8_tag,
                const_value_index: 0,
                enum_const_value_type_name_index: reader.read_u16(),
                enum_const_value_const_name_index: reader.read_u16(),
                class_info_index: 0,
                annotation_value: None,
                element_value: None,
            })
        }
        'c' => {
            Ok(RuntimeVisibleAnnotationElementValuePairElementValue {
                tag: u8_tag,
                const_value_index: 0,
                enum_const_value_type_name_index: 0,
                enum_const_value_const_name_index: 0,
                class_info_index: reader.read_u16(),
                annotation_value: None,
                element_value: None,
            })
        }
        '@' => {
            let anno = parse_runtime_visible_annotation_element_value_pairs_element_value(reader);
            if let Err(msg) = anno {
                println!("msg:{}", msg);
                return Err(msg);
            }
            let annotation_value = Some(Box::new(anno.unwrap()));
            Ok(RuntimeVisibleAnnotationElementValuePairElementValue {
                tag: u8_tag,
                const_value_index: 0,
                enum_const_value_type_name_index: 0,
                enum_const_value_const_name_index: 0,
                class_info_index: 0,
                annotation_value,
                element_value: None,
            })
        }
        _ => {
            Err(format!("Error Tag:{}", tag))
        }
    };
}